Simulate data
set.seed(3119)
sim_arguments <- list(
formula = y ~ 1 + hours + motivation + study + method,
fixed = list(hours = list(var_type = 'ordinal', levels = 0:15),
motivation = list(var_type = 'continuous', mean = 0, sd = 1),
study = list(var_type = 'factor',
levels = c('alone', 'others'),
prob = c(0.53, 0.47)),
method = list(var_type = 'factor',
levels = c('read', 'summarise', 'self-test'),
prob = c(0.3, 0.4, 0.3))),
error = list(variance = 20),
sample_size = 250,
reg_weights = c(0.6, 1.4, 1.5, 6, 6, 2)
)
df3 <- simulate_fixed(data = NULL, sim_arguments) %>%
simulate_error(sim_arguments) %>%
generate_response(sim_arguments)
test_study3 <- df3 %>%
dplyr::select(y, hours, motivation, study, method) %>%
mutate(
ID = paste("ID", 101:350, sep = ""),
score = round(y+abs(min(y))),
motivation = round(motivation, 2),
study = factor(study),
method = factor(method)
) %>%
dplyr::select(ID, score, hours, motivation, study, method)
study (two levels)
Group means
test_study3 |>
group_by(study) |>
summarise(
avg_score = round(mean(score), 2)
)
mean_alone <- filter(test_study3, study == 'alone')$score |> mean()
mean_others <- filter(test_study3, study == 'others')$score |> mean()
Plot study data
test_study3 |>
ggplot(aes(x = study, y = score, fill = study, colour = study)) +
geom_violin(alpha = 0.5) +
geom_sina(alpha = 0.5) +
theme(legend.position = 'none') +
stat_summary(colour = 'black', fun = mean, geom = 'point', size = 2) +
geom_segment(aes(x = 'alone', xend = 'others', y = mean_alone, yend = mean_others), colour = 'black') +
NULL

Plot on xy plane, dummy-coded.
contrasts(test_study3$study)
others
alone 0
others 1
alone = ref level.
xlim_lower <- -2.2
xlim_upper <- 2.2
ylim_lower <- -25
ylim_upper <- 55
test_study3 |>
mutate(study_num = ifelse(study == 'alone', 0, 1)) |>
ggplot(aes(x = study_num, y = score, fill = study, colour = study)) +
geom_jitter(alpha = 0.5, width = 0.1) +
scale_x_continuous(limits = c(xlim_lower, xlim_upper), expand = c(0, 0)) +
scale_y_continuous(limits = c(ylim_lower, ylim_upper), expand = c(0, 0)) +
geom_segment(aes(x = xlim_lower, xend = xlim_upper, y = 0, yend = 0), arrow = arrow(ends = 'both', length = unit(12, 'pt')), colour = 'black') +
geom_segment(aes(x = 0, xend = 0, y = ylim_lower, yend = ylim_upper), arrow = arrow(ends = 'both', length = unit(12, 'pt')), colour = 'black') +
geom_segment(aes(x = 0, xend = 1, y = mean_alone, yend = mean_others), colour = 'black') +
geom_segment(aes(x = 0, xend = 1, y = mean_alone, yend = mean_alone), colour = 'red', linewidth = 2) +
geom_segment(aes(x = 1, xend = 1, y = mean_alone, yend = mean_others), colour = 'red', linewidth = 2) +
NULL

Add note that the manual stuff is so you understand how coding works.
But you’ll never have to do it this laboriously.
Exact same outcome:
- manual ifelse
contr.treatment()
Model score ~ study
mod1 <- lm(score ~ study, data = test_study3)
summary(mod1)
Call:
lm(formula = score ~ study, data = test_study3)
Residuals:
Min 1Q Median 3Q Max
-22.7902 -4.7902 0.2098 5.4766 18.2098
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 22.7902 0.6603 34.52 < 2e-16 ***
studyothers 4.7332 1.0093 4.69 4.53e-06 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 7.896 on 248 degrees of freedom
Multiple R-squared: 0.08145, Adjusted R-squared: 0.07775
F-statistic: 21.99 on 1 and 248 DF, p-value: 4.525e-06
t-test
t.test(score ~ study, var.equal = T, data = test_study3)
Two Sample t-test
data: score by study
t = -4.6895, df = 248, p-value = 4.525e-06
alternative hypothesis: true difference in means between group alone and group others is not equal to 0
95 percent confidence interval:
-6.721058 -2.745251
sample estimates:
mean in group alone mean in group others
22.79021 27.52336
emmeans
(mod1_emm <- emmeans(mod1, ~study))
study emmean SE df lower.CL upper.CL
alone 22.8 0.660 248 21.5 24.1
others 27.5 0.763 248 26.0 29.0
Confidence level used: 0.95
(p_mod1_emm <- mod1_emm |> plot() +
coord_flip())

overlay emmeans on data plot
mod1_emm_tib <- mod1_emm |>
as_tibble() |>
rename(score = emmean)
test_study3 |>
ggplot(aes(x = study, y = score, fill = study, colour = study)) +
geom_violin(alpha = 0.5) +
geom_jitter(alpha = 0.5, width = 0.2) +
theme(legend.position = 'none') +
geom_errorbar(data = mod1_emm_tib, aes(ymin = lower.CL, ymax = upper.CL), colour = 'black', width = 0.2) +
geom_point(data = mod1_emm_tib, colour = 'black', size = 3) +
NULL

pairwise comparisons
levels(test_study3$study)
[1] "alone" "others"
For each comparison we want to make, assign 1 to the level we’re
interested in, and assign -1 to the level you want to compare it to. If
there are any other levels in there, assign them 0.
mod1_comparisons <- list(
'alone vs. others' = c(
1, # alone
-1 # others
)
)
contrast(
object = mod1_emm,
method = mod1_comparisons
)
contrast estimate SE df t.ratio p.value
alone vs. others -4.73 1.01 248 -4.690 <.0001
Basically reconstructs the model’s estimates. Not so interesting.
Change ref level: coefs are diff but emmeans are same
Original contrasts:
contrasts(test_study3$study)
others
alone 0
others 1
Now, let’s flip it:
contrasts(test_study3$study) <- contr.treatment(
levels(test_study3$study),
base = 2)
contrasts(test_study3$study)
alone
alone 1
others 0
plot flipped data
xlim_lower <- -2.2
xlim_upper <- 2.2
ylim_lower <- -25
ylim_upper <- 55
test_study3 |>
mutate(study_num = ifelse(study == 'alone', 1, 0)) |>
ggplot(aes(x = study_num, y = score, fill = study, colour = study)) +
geom_jitter(alpha = 0.5, width = 0.1) +
scale_x_continuous(limits = c(xlim_lower, xlim_upper), expand = c(0, 0)) +
scale_y_continuous(limits = c(ylim_lower, ylim_upper), expand = c(0, 0)) +
geom_segment(aes(x = xlim_lower, xend = xlim_upper, y = 0, yend = 0), arrow = arrow(ends = 'both', length = unit(12, 'pt')), colour = 'black') +
geom_segment(aes(x = 0, xend = 0, y = ylim_lower, yend = ylim_upper), arrow = arrow(ends = 'both', length = unit(12, 'pt')), colour = 'black') +
geom_segment(aes(x = 0, xend = 1, y = mean_others, yend = mean_alone), colour = 'black') +
geom_segment(aes(x = 0, xend = 1, y = mean_others, yend = mean_others), colour = 'red', linewidth = 2) +
geom_segment(aes(x = 1, xend = 1, y = mean_others, yend = mean_alone), colour = 'red', linewidth = 2) +
NULL

fit model (different)
mod1b <- lm(score ~ study, data = test_study3)
coef(mod1b)
(Intercept) studyalone
27.523364 -4.733155
- What is intercept? (mean when study = others)
- What is studyalone? (diff between levels: mean score when study =
alone minus mean score when study = others)
Compare this to the original coefs from mod1:
coef(mod1)
(Intercept) studyothers
22.790210 4.733155
- What is intercept? (mean when study = alone)
- What is studyothers? (diff between levels: mean score when study =
others minus mean score when study = alone)
get emmeans (same)
(mod1b_emm <- emmeans(mod1b, ~study))
study emmean SE df lower.CL upper.CL
alone 22.8 0.660 248 21.5 24.1
others 27.5 0.763 248 26.0 29.0
Confidence level used: 0.95
p_mod1b_emm <- mod1b_emm |> plot() +
coord_flip() +
ggtitle('With reference level = others')
p_mod1_emm <- p_mod1_emm + ggtitle('With reference level = alone')
p_mod1b_emm + p_mod1_emm

Identical!
method (three levels)
Group means
test_study3 |>
group_by(method) |>
summarise(
avg_score = round(mean(score), 2)
)
mean_read <- filter(test_study3, method == 'read')$score |> mean()
mean_self <- filter(test_study3, method == 'self-test')$score |> mean()
mean_summ <- filter(test_study3, method == 'summarise')$score |> mean()
Plot data
test_study3 |>
ggplot(aes(x = method, y = score, fill = method, colour = method)) +
geom_violin(alpha = 0.5) +
geom_jitter(alpha = 0.5, width = 0.2) +
theme(legend.position = 'none') +
NULL

Goal: Compare the means of each group to one another. That was easy
when we had only two groups: one straight line can go from mean to mean.
But now one straight line won’t cut it anymore.
The smallest number of lines that will connect all the means = 2.
Instead of estimating one line, we will estimate two.
And in general, if we have \(n\)
groups, we will be estimating \(n-1\)
lines.
The lines that we’re going to be estimating now:
test_study3 |>
ggplot(aes(x = method, y = score, fill = method, colour = method)) +
geom_violin(alpha = 0.5) +
geom_jitter(alpha = 0.5, width = 0.2) +
theme(legend.position = 'none') +
stat_summary(colour = 'black', fun = mean, geom = 'point', size = 2) +
# line from read to self-test:
geom_segment(colour = 'black', aes(x = 'read', xend = 'self-test', y = mean_read, yend = mean_self)) +
# line from read to summarise:
geom_segment(colour = 'black', aes(x = 'read', xend = 'summarise', y = mean_read, yend = mean_summ)) +
NULL

But I’m lying a little bit. This looks like going to go from 0 to 1
in one line, and 0 to 2 in the other line. But that’s not true – this is
just the intuition about what groups we are comparing. Really what we’re
going to do is this:
Plot dummy-coded method in xy space
Two panels: the first will be the difference between
read and self-test (the first variable), the
second will be difference between read and
summarise (the second variable).
xlim_lower <- -2.2
xlim_upper <- 2.2
ylim_lower <- -25
ylim_upper <- 55
p1 <- test_study3 |>
filter(method %in% c('read', 'self-test')) |>
mutate(method_num = ifelse(method == 'read', 0, 1)) |>
ggplot(aes(x = method_num, y = score, fill = method, colour = method)) +
geom_jitter(alpha = 0.5, width = 0.1) +
scale_x_continuous(limits = c(xlim_lower, xlim_upper), expand = c(0, 0)) +
scale_y_continuous(limits = c(ylim_lower, ylim_upper), expand = c(0, 0)) +
geom_segment(aes(x = xlim_lower, xend = xlim_upper, y = 0, yend = 0), arrow = arrow(ends = 'both', length = unit(12, 'pt')), colour = 'black') +
geom_segment(aes(x = 0, xend = 0, y = ylim_lower, yend = ylim_upper), arrow = arrow(ends = 'both', length = unit(12, 'pt')), colour = 'black') +
geom_segment(aes(x = 0, xend = 1, y = mean_read, yend = mean_self), colour = 'black') +
geom_segment(aes(x = 0, xend = 1, y = mean_read, yend = mean_read), colour = 'red', linewidth = 2) +
geom_segment(aes(x = 1, xend = 1, y = mean_read, yend = mean_self), colour = 'red', linewidth = 2) +
labs(
title = 'First line: read vs. self-test'
) +
NULL
p2 <- test_study3 |>
filter(method %in% c('read', 'summarise')) |>
mutate(method_num = ifelse(method == 'read', 0, 1)) |>
ggplot(aes(x = method_num, y = score, fill = method, colour = method)) +
geom_jitter(alpha = 0.5, width = 0.1) +
scale_x_continuous(limits = c(xlim_lower, xlim_upper), expand = c(0, 0)) +
scale_y_continuous(limits = c(ylim_lower, ylim_upper), expand = c(0, 0)) +
geom_segment(aes(x = xlim_lower, xend = xlim_upper, y = 0, yend = 0), arrow = arrow(ends = 'both', length = unit(12, 'pt')), colour = 'black') +
geom_segment(aes(x = 0, xend = 0, y = ylim_lower, yend = ylim_upper), arrow = arrow(ends = 'both', length = unit(12, 'pt')), colour = 'black') +
geom_segment(aes(x = 0, xend = 1, y = mean_read, yend = mean_summ), colour = 'black') +
geom_segment(aes(x = 0, xend = 1, y = mean_read, yend = mean_read), colour = 'red', linewidth = 2) +
geom_segment(aes(x = 1, xend = 1, y = mean_read, yend = mean_summ), colour = 'red', linewidth = 2) +
labs(
title = 'Second line: read vs. summarise'
) +
NULL
p1 + p2

Dummy-code method variable
Manual dummy-coding:
dummyCoded <- test_study3 %>%
select(ID, score, method) %>%
mutate(
method1 = ifelse(method == "self-test", 1, 0),
method2 = ifelse(method == "summarise", 1, 0))
dummyCoded
contrasts(test_study3$method)
self-test summarise
read 0 0
self-test 1 0
summarise 0 1
Model score ~ method
mod2 <- lm(score ~ method, data = dummyCoded)
summary(mod2)
Call:
lm(formula = score ~ method, data = dummyCoded)
Residuals:
Min 1Q Median 3Q Max
-23.4138 -5.3593 -0.1959 5.7496 17.8041
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 23.4138 0.8662 27.031 <2e-16 ***
methodself-test 4.1620 1.3188 3.156 0.0018 **
methodsummarise 0.7821 1.1930 0.656 0.5127
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 8.079 on 247 degrees of freedom
Multiple R-squared: 0.04224, Adjusted R-squared: 0.03448
F-statistic: 5.447 on 2 and 247 DF, p-value: 0.004845
t-tests
(Nearly) equivalent t-tests (different bc diff degrees of freedom, bc
filtering dataset. but close).
dC1 <- test_study3 |>
filter(method %in% c('read', 'self-test')) |>
mutate(method = factor(method, levels = c('read', 'self-test')))
contrasts(dC1$method) <- contr.treatment(2)
contrasts(dC1$method)
2
read 0
self-test 1
t.test(score ~ method, var.equal = T, data = dC1)
Two Sample t-test
data: score by method
t = -3.1354, df = 151, p-value = 0.002063
alternative hypothesis: true difference in means between group read and group self-test is not equal to 0
95 percent confidence interval:
-6.784657 -1.539272
sample estimates:
mean in group read mean in group self-test
23.41379 27.57576
dC2 <- test_study3 |>
filter(method %in% c('read', 'summarise')) |>
mutate(method = factor(method, levels = c('read', 'summarise')))
contrasts(dC2$method) <- contr.treatment(2)
contrasts(dC2$method)
2
read 0
summarise 1
t.test(score ~ method, var.equal = T, data = dC2)
Two Sample t-test
data: score by method
t = -0.66342, df = 182, p-value = 0.5079
alternative hypothesis: true difference in means between group read and group summarise is not equal to 0
95 percent confidence interval:
-3.108082 1.543915
sample estimates:
mean in group read mean in group summarise
23.41379 24.19588
emmeans
Before, I said that the smallest number of lines we need to compare
three groups is two. But this actually means that one of the possible
comparisons is left out.
(??? above the missing line between self-test and summarise)
Options:
- make something else ref level. Then we get that comparison. But then
we’re missing a different third comparison.
- get emmeans, and once we’re in the outcome space, we can compute
differences between whatever we want! yay!
(mod2_emm <- emmeans(mod2, ~method))
method emmean SE df lower.CL upper.CL
read 23.4 0.866 247 21.7 25.1
self-test 27.6 0.994 247 25.6 29.5
summarise 24.2 0.820 247 22.6 25.8
Confidence level used: 0.95
mod2_emm |> plot() +
coord_flip()

overlay emmeans on data plot
mod2_emm_tib <- mod2_emm |>
as_tibble() |>
rename(score = emmean)
test_study3 |>
ggplot(aes(x = method, y = score, fill = method, colour = method)) +
geom_violin(alpha = 0.5) +
geom_jitter(alpha = 0.5, width = 0.2) +
theme(legend.position = 'none') +
geom_errorbar(data = mod2_emm_tib, aes(ymin = lower.CL, ymax = upper.CL), colour = 'black', width = 0.2) +
geom_point(data = mod2_emm_tib, colour = 'black', size = 3) +
NULL

pairwise comparisons
levels(test_study3$method)
[1] "read" "self-test" "summarise"
Assign 1 to the level we are interested in, and -1 to the baseline we
want to compare it to.
mod2_comparisons <- list(
'Self-test vs. Read' = c(-1, 1, 0), # read self-test summarise, in that order
'Summarise vs. Read' = c(-1, 0, 1),
'Self-test vs. Summarise' = c(0, 1, -1),
'mean(Self-test, summarise) vs. Read' = c(-1, 0.5, 0.5) # weights must sum to 0 -- pools self-test and summarise tog and pits that mean against mean of Read
)
1 - 1
mean(Self-test) - mean(Read) = positive, bc self-test is
larger than read
(mod2_comparisons_test <- contrast(mod2_emm, mod2_comparisons))
contrast estimate SE df t.ratio p.value
Self-test vs. Read 4.162 1.32 247 3.156 0.0018
Summarise vs. Read 0.782 1.19 247 0.656 0.5127
Self-test vs. Summarise 3.380 1.29 247 2.622 0.0093
mean(Self-test, summarise) vs. Read 2.472 1.08 247 2.290 0.0229
contrast() gives us the p-values for each difference. We
can also get the 95% CIs of each difference by giving the outcome of
contrast() into confint().
confint(mod2_comparisons_test)
contrast estimate SE df lower.CL upper.CL
Self-test vs. Read 4.162 1.32 247 1.564 6.76
Summarise vs. Read 0.782 1.19 247 -1.568 3.13
Self-test vs. Summarise 3.380 1.29 247 0.841 5.92
mean(Self-test, summarise) vs. Read 2.472 1.08 247 0.345 4.60
Confidence level used: 0.95
LS0tCnRpdGxlOiAiRVAgcGxheWdyb3VuZCDigJMgMDYgY2F0ZWcgZHVtbXkiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQotLS0KCmBgYHtyIHNldHVwfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoZW1tZWFucykKbGlicmFyeShzaW1nbG0pCmBgYAoKCiMgU2ltdWxhdGUgZGF0YQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kc2V0LnNlZWQoMzExOSkgCgpzaW1fYXJndW1lbnRzIDwtIGxpc3QoCiAgZm9ybXVsYSA9IHkgfiAxICsgaG91cnMgKyBtb3RpdmF0aW9uICsgc3R1ZHkgKyBtZXRob2QsCiAgZml4ZWQgPSBsaXN0KGhvdXJzID0gbGlzdCh2YXJfdHlwZSA9ICdvcmRpbmFsJywgbGV2ZWxzID0gMDoxNSksCiAgICAgICAgICAgICAgIG1vdGl2YXRpb24gPSBsaXN0KHZhcl90eXBlID0gJ2NvbnRpbnVvdXMnLCBtZWFuID0gMCwgc2QgPSAxKSwKICAgICAgICAgICAgICAgc3R1ZHkgPSBsaXN0KHZhcl90eXBlID0gJ2ZhY3RvcicsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygnYWxvbmUnLCAnb3RoZXJzJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9iID0gYygwLjUzLCAwLjQ3KSksCiAgICAgICAgICAgICAgIG1ldGhvZCA9IGxpc3QodmFyX3R5cGUgPSAnZmFjdG9yJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCdyZWFkJywgJ3N1bW1hcmlzZScsICdzZWxmLXRlc3QnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2IgPSBjKDAuMywgMC40LCAwLjMpKSksCiAgZXJyb3IgPSBsaXN0KHZhcmlhbmNlID0gMjApLAogIHNhbXBsZV9zaXplID0gMjUwLAogIHJlZ193ZWlnaHRzID0gYygwLjYsIDEuNCwgMS41LCA2LCA2LCAyKQopCgpkZjMgPC0gc2ltdWxhdGVfZml4ZWQoZGF0YSA9IE5VTEwsIHNpbV9hcmd1bWVudHMpICU+JQogIHNpbXVsYXRlX2Vycm9yKHNpbV9hcmd1bWVudHMpICU+JQogIGdlbmVyYXRlX3Jlc3BvbnNlKHNpbV9hcmd1bWVudHMpCgp0ZXN0X3N0dWR5MyA8LSBkZjMgJT4lCiAgZHBseXI6OnNlbGVjdCh5LCBob3VycywgbW90aXZhdGlvbiwgc3R1ZHksIG1ldGhvZCkgJT4lCiAgbXV0YXRlKAogICAgSUQgPSBwYXN0ZSgiSUQiLCAxMDE6MzUwLCBzZXAgPSAiIiksCiAgICBzY29yZSA9IHJvdW5kKHkrYWJzKG1pbih5KSkpLAogICAgbW90aXZhdGlvbiA9IHJvdW5kKG1vdGl2YXRpb24sIDIpLAogICAgc3R1ZHkgPSBmYWN0b3Ioc3R1ZHkpLAogICAgbWV0aG9kID0gZmFjdG9yKG1ldGhvZCkKICApICU+JQogIGRwbHlyOjpzZWxlY3QoSUQsIHNjb3JlLCBob3VycywgbW90aXZhdGlvbiwgc3R1ZHksIG1ldGhvZCkKCmBgYAoKCiMgYHN0dWR5YCAodHdvIGxldmVscykKCiMjIEdyb3VwIG1lYW5zCgpgYGB7cn0KdGVzdF9zdHVkeTMgfD4KICBncm91cF9ieShzdHVkeSkgfD4KICBzdW1tYXJpc2UoCiAgICBhdmdfc2NvcmUgPSByb3VuZChtZWFuKHNjb3JlKSwgMikKICApCgptZWFuX2Fsb25lIDwtIGZpbHRlcih0ZXN0X3N0dWR5Mywgc3R1ZHkgPT0gJ2Fsb25lJykkc2NvcmUgfD4gbWVhbigpCm1lYW5fb3RoZXJzIDwtIGZpbHRlcih0ZXN0X3N0dWR5Mywgc3R1ZHkgPT0gJ290aGVycycpJHNjb3JlIHw+IG1lYW4oKQpgYGAKCgojIyBQbG90IGBzdHVkeWAgZGF0YQoKYGBge3J9CnRlc3Rfc3R1ZHkzIHw+CiAgZ2dwbG90KGFlcyh4ID0gc3R1ZHksIHkgPSBzY29yZSwgZmlsbCA9IHN0dWR5LCBjb2xvdXIgPSBzdHVkeSkpICsKICBnZW9tX3Zpb2xpbihhbHBoYSA9IDAuNSkgKwogIGdlb21fc2luYShhbHBoYSA9IDAuNSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykgKwogIHN0YXRfc3VtbWFyeShjb2xvdXIgPSAnYmxhY2snLCBmdW4gPSBtZWFuLCBnZW9tID0gJ3BvaW50Jywgc2l6ZSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAnYWxvbmUnLCB4ZW5kID0gJ290aGVycycsIHkgPSBtZWFuX2Fsb25lLCB5ZW5kID0gbWVhbl9vdGhlcnMpLCBjb2xvdXIgPSAnYmxhY2snKSArCiAgTlVMTApgYGAKClBsb3Qgb24geHkgcGxhbmUsIGR1bW15LWNvZGVkLgoKYGBge3J9CmNvbnRyYXN0cyh0ZXN0X3N0dWR5MyRzdHVkeSkKYGBgCgoKYGFsb25lYCA9IHJlZiBsZXZlbC4KCmBgYHtyfQp4bGltX2xvd2VyIDwtIC0yLjIKeGxpbV91cHBlciA8LSAgMi4yCnlsaW1fbG93ZXIgPC0gLTI1CnlsaW1fdXBwZXIgPC0gIDU1Cgp0ZXN0X3N0dWR5MyB8PgogIG11dGF0ZShzdHVkeV9udW0gPSBpZmVsc2Uoc3R1ZHkgPT0gJ2Fsb25lJywgMCwgMSkpIHw+CiAgZ2dwbG90KGFlcyh4ID0gc3R1ZHlfbnVtLCB5ID0gc2NvcmUsIGZpbGwgPSBzdHVkeSwgY29sb3VyID0gc3R1ZHkpKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUsIHdpZHRoID0gMC4xKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoeGxpbV9sb3dlciwgeGxpbV91cHBlciksIGV4cGFuZCA9IGMoMCwgMCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYyh5bGltX2xvd2VyLCB5bGltX3VwcGVyKSwgZXhwYW5kID0gYygwLCAwKSkgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IHhsaW1fbG93ZXIsIHhlbmQgPSB4bGltX3VwcGVyLCB5ID0gMCwgeWVuZCA9IDApLCBhcnJvdyA9IGFycm93KGVuZHMgPSAnYm90aCcsIGxlbmd0aCA9IHVuaXQoMTIsICdwdCcpKSwgY29sb3VyID0gJ2JsYWNrJykgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHhlbmQgPSAwLCB5ID0geWxpbV9sb3dlciwgeWVuZCA9IHlsaW1fdXBwZXIpLCBhcnJvdyA9IGFycm93KGVuZHMgPSAnYm90aCcsIGxlbmd0aCA9IHVuaXQoMTIsICdwdCcpKSwgY29sb3VyID0gJ2JsYWNrJykgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHhlbmQgPSAxLCB5ID0gbWVhbl9hbG9uZSwgeWVuZCA9IG1lYW5fb3RoZXJzKSwgY29sb3VyID0gJ2JsYWNrJykgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHhlbmQgPSAxLCB5ID0gbWVhbl9hbG9uZSwgeWVuZCA9IG1lYW5fYWxvbmUpLCBjb2xvdXIgPSAncmVkJywgbGluZXdpZHRoID0gMikgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDEsIHhlbmQgPSAxLCB5ID0gbWVhbl9hbG9uZSwgeWVuZCA9IG1lYW5fb3RoZXJzKSwgY29sb3VyID0gJ3JlZCcsIGxpbmV3aWR0aCA9IDIpICsKICBOVUxMCmBgYAoKCkFkZCBub3RlIHRoYXQgdGhlIG1hbnVhbCBzdHVmZiBpcyBzbyB5b3UgdW5kZXJzdGFuZCBob3cgY29kaW5nIHdvcmtzLgpCdXQgeW91J2xsIG5ldmVyIGhhdmUgdG8gZG8gaXQgdGhpcyBsYWJvcmlvdXNseS4KCkV4YWN0IHNhbWUgb3V0Y29tZToKCi0gbWFudWFsIGlmZWxzZQotIGBjb250ci50cmVhdG1lbnQoKWAKCgoKCiMjIE1vZGVsIGBzY29yZSB+IHN0dWR5YAoKYGBge3J9Cm1vZDEgPC0gbG0oc2NvcmUgfiBzdHVkeSwgZGF0YSA9IHRlc3Rfc3R1ZHkzKQpzdW1tYXJ5KG1vZDEpCmBgYAoKIyMjIHQtdGVzdAoKYGBge3J9CnQudGVzdChzY29yZSB+IHN0dWR5LCB2YXIuZXF1YWwgPSBULCBkYXRhID0gdGVzdF9zdHVkeTMpCmBgYAoKIyMgZW1tZWFucwoKYGBge3J9Cihtb2QxX2VtbSA8LSBlbW1lYW5zKG1vZDEsIH5zdHVkeSkpCmBgYAoKCmBgYHtyfQoocF9tb2QxX2VtbSA8LSBtb2QxX2VtbSB8PiBwbG90KCkgKwogIGNvb3JkX2ZsaXAoKSkKYGBgCgojIyMgb3ZlcmxheSBlbW1lYW5zIG9uIGRhdGEgcGxvdAoKYGBge3J9Cm1vZDFfZW1tX3RpYiA8LSBtb2QxX2VtbSB8PgogIGFzX3RpYmJsZSgpIHw+CiAgcmVuYW1lKHNjb3JlID0gZW1tZWFuKQpgYGAKCmBgYHtyfQp0ZXN0X3N0dWR5MyB8PgogIGdncGxvdChhZXMoeCA9IHN0dWR5LCB5ID0gc2NvcmUsIGZpbGwgPSBzdHVkeSwgY29sb3VyID0gc3R1ZHkpKSArCiAgZ2VvbV92aW9saW4oYWxwaGEgPSAwLjUpICsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuNSwgd2lkdGggPSAwLjIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpICsKICBnZW9tX2Vycm9yYmFyKGRhdGEgPSBtb2QxX2VtbV90aWIsIGFlcyh5bWluID0gbG93ZXIuQ0wsIHltYXggPSB1cHBlci5DTCksIGNvbG91ciA9ICdibGFjaycsIHdpZHRoID0gMC4yKSArCiAgZ2VvbV9wb2ludChkYXRhID0gbW9kMV9lbW1fdGliLCBjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMykgKwogIE5VTEwKYGBgCgoKIyMjIHBhaXJ3aXNlIGNvbXBhcmlzb25zCgpgYGB7cn0KbGV2ZWxzKHRlc3Rfc3R1ZHkzJHN0dWR5KQpgYGAKCkZvciBlYWNoIGNvbXBhcmlzb24gd2Ugd2FudCB0byBtYWtlLCBhc3NpZ24gMSB0byB0aGUgbGV2ZWwgd2UncmUgaW50ZXJlc3RlZCBpbiwgYW5kIGFzc2lnbiAtMSB0byB0aGUgbGV2ZWwgeW91IHdhbnQgdG8gY29tcGFyZSBpdCB0by4KSWYgdGhlcmUgYXJlIGFueSBvdGhlciBsZXZlbHMgaW4gdGhlcmUsIGFzc2lnbiB0aGVtIDAuCgpgYGB7cn0KbW9kMV9jb21wYXJpc29ucyA8LSBsaXN0KAogICdhbG9uZSB2cy4gb3RoZXJzJyA9IGMoCiAgICAxLCAgICMgYWxvbmUKICAgIC0xICAgIyBvdGhlcnMKICApCikKCmNvbnRyYXN0KAogIG9iamVjdCA9IG1vZDFfZW1tLAogIG1ldGhvZCA9IG1vZDFfY29tcGFyaXNvbnMKKQpgYGAKCkJhc2ljYWxseSByZWNvbnN0cnVjdHMgdGhlIG1vZGVsJ3MgZXN0aW1hdGVzLgpOb3Qgc28gaW50ZXJlc3RpbmcuCgoKIyMgQ2hhbmdlIHJlZiBsZXZlbDogY29lZnMgYXJlIGRpZmYgYnV0IGVtbWVhbnMgYXJlIHNhbWUKCk9yaWdpbmFsIGNvbnRyYXN0czoKCmBgYHtyfQpjb250cmFzdHModGVzdF9zdHVkeTMkc3R1ZHkpCmBgYAoKTm93LCBsZXQncyBmbGlwIGl0OgoKYGBge3J9CmNvbnRyYXN0cyh0ZXN0X3N0dWR5MyRzdHVkeSkgPC0gY29udHIudHJlYXRtZW50KAogIGxldmVscyh0ZXN0X3N0dWR5MyRzdHVkeSksIAogIGJhc2UgPSAyKQpjb250cmFzdHModGVzdF9zdHVkeTMkc3R1ZHkpCmBgYAoKIyMjIHBsb3QgZmxpcHBlZCBkYXRhCgpgYGB7cn0KeGxpbV9sb3dlciA8LSAtMi4yCnhsaW1fdXBwZXIgPC0gIDIuMgp5bGltX2xvd2VyIDwtIC0yNQp5bGltX3VwcGVyIDwtICA1NQoKdGVzdF9zdHVkeTMgfD4KICBtdXRhdGUoc3R1ZHlfbnVtID0gaWZlbHNlKHN0dWR5ID09ICdhbG9uZScsIDEsIDApKSB8PgogIGdncGxvdChhZXMoeCA9IHN0dWR5X251bSwgeSA9IHNjb3JlLCBmaWxsID0gc3R1ZHksIGNvbG91ciA9IHN0dWR5KSkgKwogIGdlb21faml0dGVyKGFscGhhID0gMC41LCB3aWR0aCA9IDAuMSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKHhsaW1fbG93ZXIsIHhsaW1fdXBwZXIpLCBleHBhbmQgPSBjKDAsIDApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoeWxpbV9sb3dlciwgeWxpbV91cHBlciksIGV4cGFuZCA9IGMoMCwgMCkpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSB4bGltX2xvd2VyLCB4ZW5kID0geGxpbV91cHBlciwgeSA9IDAsIHllbmQgPSAwKSwgYXJyb3cgPSBhcnJvdyhlbmRzID0gJ2JvdGgnLCBsZW5ndGggPSB1bml0KDEyLCAncHQnKSksIGNvbG91ciA9ICdibGFjaycpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB4ZW5kID0gMCwgeSA9IHlsaW1fbG93ZXIsIHllbmQgPSB5bGltX3VwcGVyKSwgYXJyb3cgPSBhcnJvdyhlbmRzID0gJ2JvdGgnLCBsZW5ndGggPSB1bml0KDEyLCAncHQnKSksIGNvbG91ciA9ICdibGFjaycpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB4ZW5kID0gMSwgeSA9IG1lYW5fb3RoZXJzLCB5ZW5kID0gbWVhbl9hbG9uZSksIGNvbG91ciA9ICdibGFjaycpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB4ZW5kID0gMSwgeSA9IG1lYW5fb3RoZXJzLCB5ZW5kID0gbWVhbl9vdGhlcnMpLCBjb2xvdXIgPSAncmVkJywgbGluZXdpZHRoID0gMikgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDEsIHhlbmQgPSAxLCB5ID0gbWVhbl9vdGhlcnMsIHllbmQgPSBtZWFuX2Fsb25lKSwgY29sb3VyID0gJ3JlZCcsIGxpbmV3aWR0aCA9IDIpICsKICBOVUxMCmBgYAoKIyMjIGZpdCBtb2RlbCAoZGlmZmVyZW50KQoKYGBge3J9Cm1vZDFiIDwtIGxtKHNjb3JlIH4gc3R1ZHksIGRhdGEgPSB0ZXN0X3N0dWR5MykKY29lZihtb2QxYikKYGBgCgotIFdoYXQgaXMgaW50ZXJjZXB0PyAobWVhbiB3aGVuIHN0dWR5ID0gb3RoZXJzKQotIFdoYXQgaXMgc3R1ZHlhbG9uZT8gKGRpZmYgYmV0d2VlbiBsZXZlbHM6IG1lYW4gc2NvcmUgd2hlbiBzdHVkeSA9IGFsb25lIG1pbnVzIG1lYW4gc2NvcmUgd2hlbiBzdHVkeSA9IG90aGVycykKCkNvbXBhcmUgdGhpcyB0byB0aGUgb3JpZ2luYWwgY29lZnMgZnJvbSBgbW9kMWA6CgpgYGB7cn0KY29lZihtb2QxKQpgYGAKCi0gV2hhdCBpcyBpbnRlcmNlcHQ/IChtZWFuIHdoZW4gc3R1ZHkgPSBhbG9uZSkKLSBXaGF0IGlzIHN0dWR5b3RoZXJzPyAoZGlmZiBiZXR3ZWVuIGxldmVsczogbWVhbiBzY29yZSB3aGVuIHN0dWR5ID0gb3RoZXJzIG1pbnVzIG1lYW4gc2NvcmUgd2hlbiBzdHVkeSA9IGFsb25lKQoKCiMjIyBnZXQgZW1tZWFucyAoc2FtZSkKCmBgYHtyfQoobW9kMWJfZW1tIDwtIGVtbWVhbnMobW9kMWIsIH5zdHVkeSkpCmBgYAoKYGBge3J9CnBfbW9kMWJfZW1tIDwtIG1vZDFiX2VtbSB8PiBwbG90KCkgKwogIGNvb3JkX2ZsaXAoKSArCiAgZ2d0aXRsZSgnV2l0aCByZWZlcmVuY2UgbGV2ZWwgPSBvdGhlcnMnKQoKcF9tb2QxX2VtbSA8LSBwX21vZDFfZW1tICsgZ2d0aXRsZSgnV2l0aCByZWZlcmVuY2UgbGV2ZWwgPSBhbG9uZScpCgpwX21vZDFiX2VtbSArIHBfbW9kMV9lbW0KYGBgCgpJZGVudGljYWwhCgoKIyBgbWV0aG9kYCAodGhyZWUgbGV2ZWxzKQoKIyMgR3JvdXAgbWVhbnMKCmBgYHtyfQp0ZXN0X3N0dWR5MyB8PgogIGdyb3VwX2J5KG1ldGhvZCkgfD4KICBzdW1tYXJpc2UoCiAgICBhdmdfc2NvcmUgPSByb3VuZChtZWFuKHNjb3JlKSwgMikKICApCgptZWFuX3JlYWQgPC0gZmlsdGVyKHRlc3Rfc3R1ZHkzLCBtZXRob2QgPT0gJ3JlYWQnKSRzY29yZSB8PiBtZWFuKCkKbWVhbl9zZWxmIDwtIGZpbHRlcih0ZXN0X3N0dWR5MywgbWV0aG9kID09ICdzZWxmLXRlc3QnKSRzY29yZSB8PiBtZWFuKCkKbWVhbl9zdW1tIDwtIGZpbHRlcih0ZXN0X3N0dWR5MywgbWV0aG9kID09ICdzdW1tYXJpc2UnKSRzY29yZSB8PiBtZWFuKCkKYGBgCgoKIyMgUGxvdCBkYXRhCgpgYGB7cn0KdGVzdF9zdHVkeTMgfD4KICBnZ3Bsb3QoYWVzKHggPSBtZXRob2QsIHkgPSBzY29yZSwgZmlsbCA9IG1ldGhvZCwgY29sb3VyID0gbWV0aG9kKSkgKwogIGdlb21fdmlvbGluKGFscGhhID0gMC41KSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUsIHdpZHRoID0gMC4yKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKSArCiAgTlVMTApgYGAKCkdvYWw6IENvbXBhcmUgdGhlIG1lYW5zIG9mIGVhY2ggZ3JvdXAgdG8gb25lIGFub3RoZXIuClRoYXQgd2FzIGVhc3kgd2hlbiB3ZSBoYWQgb25seSB0d28gZ3JvdXBzOiBvbmUgc3RyYWlnaHQgbGluZSBjYW4gZ28gZnJvbSBtZWFuIHRvIG1lYW4uCkJ1dCBub3cgb25lIHN0cmFpZ2h0IGxpbmUgd29uJ3QgY3V0IGl0IGFueW1vcmUuCgpUaGUgc21hbGxlc3QgbnVtYmVyIG9mIGxpbmVzIHRoYXQgd2lsbCBjb25uZWN0IGFsbCB0aGUgbWVhbnMgPSAyLgpJbnN0ZWFkIG9mIGVzdGltYXRpbmcgb25lIGxpbmUsIHdlIHdpbGwgZXN0aW1hdGUgdHdvLgoKQW5kIGluIGdlbmVyYWwsIGlmIHdlIGhhdmUgJG4kIGdyb3Vwcywgd2Ugd2lsbCBiZSBlc3RpbWF0aW5nICRuLTEkIGxpbmVzLgoKVGhlIGxpbmVzIHRoYXQgd2UncmUgZ29pbmcgdG8gYmUgZXN0aW1hdGluZyBub3c6CgpgYGB7cn0KdGVzdF9zdHVkeTMgfD4KICBnZ3Bsb3QoYWVzKHggPSBtZXRob2QsIHkgPSBzY29yZSwgZmlsbCA9IG1ldGhvZCwgY29sb3VyID0gbWV0aG9kKSkgKwogIGdlb21fdmlvbGluKGFscGhhID0gMC41KSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUsIHdpZHRoID0gMC4yKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKSArCiAgc3RhdF9zdW1tYXJ5KGNvbG91ciA9ICdibGFjaycsIGZ1biA9IG1lYW4sIGdlb20gPSAncG9pbnQnLCBzaXplID0gMikgKwogICMgbGluZSBmcm9tIHJlYWQgdG8gc2VsZi10ZXN0OgogIGdlb21fc2VnbWVudChjb2xvdXIgPSAnYmxhY2snLCBhZXMoeCA9ICdyZWFkJywgeGVuZCA9ICdzZWxmLXRlc3QnLCB5ID0gbWVhbl9yZWFkLCB5ZW5kID0gbWVhbl9zZWxmKSkgKwogICMgbGluZSBmcm9tIHJlYWQgdG8gc3VtbWFyaXNlOgogIGdlb21fc2VnbWVudChjb2xvdXIgPSAnYmxhY2snLCBhZXMoeCA9ICdyZWFkJywgeGVuZCA9ICdzdW1tYXJpc2UnLCB5ID0gbWVhbl9yZWFkLCB5ZW5kID0gbWVhbl9zdW1tKSkgKwogIE5VTEwKYGBgCgoKQnV0IEknbSBseWluZyBhIGxpdHRsZSBiaXQuClRoaXMgbG9va3MgbGlrZSBnb2luZyB0byBnbyBmcm9tIDAgdG8gMSBpbiBvbmUgbGluZSwgYW5kIDAgdG8gMiBpbiB0aGUgb3RoZXIgbGluZS4KQnV0IHRoYXQncyBub3QgdHJ1ZSAtLSB0aGlzIGlzIGp1c3QgdGhlIGludHVpdGlvbiBhYm91dCB3aGF0IGdyb3VwcyB3ZSBhcmUgY29tcGFyaW5nLgpSZWFsbHkgd2hhdCB3ZSdyZSBnb2luZyB0byBkbyBpcyB0aGlzOgoKCiMjIFBsb3QgZHVtbXktY29kZWQgYG1ldGhvZGAgaW4geHkgc3BhY2UKClR3byBwYW5lbHM6IHRoZSBmaXJzdCB3aWxsIGJlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYHJlYWRgIGFuZCBgc2VsZi10ZXN0YCAodGhlIGZpcnN0IHZhcmlhYmxlKSwgdGhlIHNlY29uZCB3aWxsIGJlIGRpZmZlcmVuY2UgYmV0d2VlbiBgcmVhZGAgYW5kIGBzdW1tYXJpc2VgICh0aGUgc2Vjb25kIHZhcmlhYmxlKS4KCmBgYHtyIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNH0KeGxpbV9sb3dlciA8LSAtMi4yCnhsaW1fdXBwZXIgPC0gIDIuMgp5bGltX2xvd2VyIDwtIC0yNQp5bGltX3VwcGVyIDwtICA1NQoKcDEgPC0gdGVzdF9zdHVkeTMgfD4gCiAgZmlsdGVyKG1ldGhvZCAlaW4lIGMoJ3JlYWQnLCAnc2VsZi10ZXN0JykpIHw+CiAgbXV0YXRlKG1ldGhvZF9udW0gPSBpZmVsc2UobWV0aG9kID09ICdyZWFkJywgMCwgMSkpIHw+CiAgZ2dwbG90KGFlcyh4ID0gbWV0aG9kX251bSwgeSA9IHNjb3JlLCBmaWxsID0gbWV0aG9kLCBjb2xvdXIgPSBtZXRob2QpKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUsIHdpZHRoID0gMC4xKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoeGxpbV9sb3dlciwgeGxpbV91cHBlciksIGV4cGFuZCA9IGMoMCwgMCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYyh5bGltX2xvd2VyLCB5bGltX3VwcGVyKSwgZXhwYW5kID0gYygwLCAwKSkgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IHhsaW1fbG93ZXIsIHhlbmQgPSB4bGltX3VwcGVyLCB5ID0gMCwgeWVuZCA9IDApLCBhcnJvdyA9IGFycm93KGVuZHMgPSAnYm90aCcsIGxlbmd0aCA9IHVuaXQoMTIsICdwdCcpKSwgY29sb3VyID0gJ2JsYWNrJykgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHhlbmQgPSAwLCB5ID0geWxpbV9sb3dlciwgeWVuZCA9IHlsaW1fdXBwZXIpLCBhcnJvdyA9IGFycm93KGVuZHMgPSAnYm90aCcsIGxlbmd0aCA9IHVuaXQoMTIsICdwdCcpKSwgY29sb3VyID0gJ2JsYWNrJykgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHhlbmQgPSAxLCB5ID0gbWVhbl9yZWFkLCB5ZW5kID0gbWVhbl9zZWxmKSwgY29sb3VyID0gJ2JsYWNrJykgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHhlbmQgPSAxLCB5ID0gbWVhbl9yZWFkLCB5ZW5kID0gbWVhbl9yZWFkKSwgY29sb3VyID0gJ3JlZCcsIGxpbmV3aWR0aCA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAxLCB4ZW5kID0gMSwgeSA9IG1lYW5fcmVhZCwgeWVuZCA9IG1lYW5fc2VsZiksIGNvbG91ciA9ICdyZWQnLCBsaW5ld2lkdGggPSAyKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0ZpcnN0IGxpbmU6IHNlbGYtdGVzdCB2cy4gcmVhZCcKICApICsKICBOVUxMCgpwMiA8LSB0ZXN0X3N0dWR5MyB8PiAKICBmaWx0ZXIobWV0aG9kICVpbiUgYygncmVhZCcsICdzdW1tYXJpc2UnKSkgfD4KICBtdXRhdGUobWV0aG9kX251bSA9IGlmZWxzZShtZXRob2QgPT0gJ3JlYWQnLCAwLCAxKSkgfD4KICBnZ3Bsb3QoYWVzKHggPSBtZXRob2RfbnVtLCB5ID0gc2NvcmUsIGZpbGwgPSBtZXRob2QsIGNvbG91ciA9IG1ldGhvZCkpICsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuNSwgd2lkdGggPSAwLjEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYyh4bGltX2xvd2VyLCB4bGltX3VwcGVyKSwgZXhwYW5kID0gYygwLCAwKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKHlsaW1fbG93ZXIsIHlsaW1fdXBwZXIpLCBleHBhbmQgPSBjKDAsIDApKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0geGxpbV9sb3dlciwgeGVuZCA9IHhsaW1fdXBwZXIsIHkgPSAwLCB5ZW5kID0gMCksIGFycm93ID0gYXJyb3coZW5kcyA9ICdib3RoJywgbGVuZ3RoID0gdW5pdCgxMiwgJ3B0JykpLCBjb2xvdXIgPSAnYmxhY2snKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeGVuZCA9IDAsIHkgPSB5bGltX2xvd2VyLCB5ZW5kID0geWxpbV91cHBlciksIGFycm93ID0gYXJyb3coZW5kcyA9ICdib3RoJywgbGVuZ3RoID0gdW5pdCgxMiwgJ3B0JykpLCBjb2xvdXIgPSAnYmxhY2snKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeGVuZCA9IDEsIHkgPSBtZWFuX3JlYWQsIHllbmQgPSBtZWFuX3N1bW0pLCBjb2xvdXIgPSAnYmxhY2snKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeGVuZCA9IDEsIHkgPSBtZWFuX3JlYWQsIHllbmQgPSBtZWFuX3JlYWQpLCBjb2xvdXIgPSAncmVkJywgbGluZXdpZHRoID0gMikgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDEsIHhlbmQgPSAxLCB5ID0gbWVhbl9yZWFkLCB5ZW5kID0gbWVhbl9zdW1tKSwgY29sb3VyID0gJ3JlZCcsIGxpbmV3aWR0aCA9IDIpICsKICBsYWJzKAogICAgdGl0bGUgPSAnU2Vjb25kIGxpbmU6IHN1bW1hcmlzZSB2cy4gcmVhZCcKICApICsgIAogIE5VTEwKCnAxICsgcDIKYGBgCgoKIyMgRHVtbXktY29kZSBgbWV0aG9kYCB2YXJpYWJsZQoKTWFudWFsIGR1bW15LWNvZGluZzoKCmBgYHtyfQpkdW1teUNvZGVkIDwtIHRlc3Rfc3R1ZHkzICU+JQogIHNlbGVjdChJRCwgc2NvcmUsIG1ldGhvZCkgJT4lCiAgbXV0YXRlKAogICAgbWV0aG9kMSA9IGlmZWxzZShtZXRob2QgPT0gInNlbGYtdGVzdCIsIDEsIDApLAogICAgbWV0aG9kMiA9IGlmZWxzZShtZXRob2QgPT0gInN1bW1hcmlzZSIsIDEsIDApKQoKZHVtbXlDb2RlZApgYGAKCgpgYGB7cn0KY29udHJhc3RzKHRlc3Rfc3R1ZHkzJG1ldGhvZCkKYGBgCgoKCgojIyBNb2RlbCBgc2NvcmUgfiBtZXRob2RgCgpgYGB7cn0KbW9kMiA8LSBsbShzY29yZSB+IG1ldGhvZCwgZGF0YSA9IGR1bW15Q29kZWQpCnN1bW1hcnkobW9kMikKYGBgCgoKIyMjIHQtdGVzdHMKCihOZWFybHkpIGVxdWl2YWxlbnQgdC10ZXN0cyAoZGlmZmVyZW50IGJjIGRpZmYgZGVncmVlcyBvZiBmcmVlZG9tLCBiYyBmaWx0ZXJpbmcgZGF0YXNldC4gYnV0IGNsb3NlKS4KCmBgYHtyfQpkQzEgPC0gdGVzdF9zdHVkeTMgfD4gCiAgZmlsdGVyKG1ldGhvZCAlaW4lIGMoJ3JlYWQnLCAnc2VsZi10ZXN0JykpIHw+CiAgbXV0YXRlKG1ldGhvZCA9IGZhY3RvcihtZXRob2QsIGxldmVscyA9IGMoJ3JlYWQnLCAnc2VsZi10ZXN0JykpKQpjb250cmFzdHMoZEMxJG1ldGhvZCkgPC0gY29udHIudHJlYXRtZW50KDIpCmNvbnRyYXN0cyhkQzEkbWV0aG9kKQoKdC50ZXN0KHNjb3JlIH4gbWV0aG9kLCB2YXIuZXF1YWwgPSBULCBkYXRhID0gZEMxKQpgYGAKCgpgYGB7cn0KZEMyIDwtIHRlc3Rfc3R1ZHkzIHw+IAogIGZpbHRlcihtZXRob2QgJWluJSBjKCdyZWFkJywgJ3N1bW1hcmlzZScpKSB8PgogIG11dGF0ZShtZXRob2QgPSBmYWN0b3IobWV0aG9kLCBsZXZlbHMgPSBjKCdyZWFkJywgJ3N1bW1hcmlzZScpKSkKY29udHJhc3RzKGRDMiRtZXRob2QpIDwtIGNvbnRyLnRyZWF0bWVudCgyKQpjb250cmFzdHMoZEMyJG1ldGhvZCkKCnQudGVzdChzY29yZSB+IG1ldGhvZCwgdmFyLmVxdWFsID0gVCwgZGF0YSA9IGRDMikKYGBgCgoKIyMgZW1tZWFucyAKCkJlZm9yZSwgSSBzYWlkIHRoYXQgdGhlIHNtYWxsZXN0IG51bWJlciBvZiBsaW5lcyB3ZSBuZWVkIHRvIGNvbXBhcmUgdGhyZWUgZ3JvdXBzIGlzIHR3by4KQnV0IHRoaXMgYWN0dWFsbHkgbWVhbnMgdGhhdCBvbmUgb2YgdGhlIHBvc3NpYmxlIGNvbXBhcmlzb25zIGlzIGxlZnQgb3V0LgoKKD8/PyBhYm92ZSB0aGUgbWlzc2luZyBsaW5lIGJldHdlZW4gc2VsZi10ZXN0IGFuZCBzdW1tYXJpc2UpCgpPcHRpb25zOgoKLSBtYWtlIHNvbWV0aGluZyBlbHNlIHJlZiBsZXZlbC4gVGhlbiB3ZSBnZXQgdGhhdCBjb21wYXJpc29uLiBCdXQgdGhlbiB3ZSdyZSBtaXNzaW5nIGEgZGlmZmVyZW50IHRoaXJkIGNvbXBhcmlzb24uCi0gZ2V0IGVtbWVhbnMsIGFuZCBvbmNlIHdlJ3JlIGluIHRoZSBvdXRjb21lIHNwYWNlLCB3ZSBjYW4gY29tcHV0ZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHdoYXRldmVyIHdlIHdhbnQhIHlheSEKCgpgYGB7cn0KKG1vZDJfZW1tIDwtIGVtbWVhbnMobW9kMiwgfm1ldGhvZCkpCmBgYAoKCmBgYHtyfQptb2QyX2VtbSB8PiBwbG90KCkgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCgojIyMgb3ZlcmxheSBlbW1lYW5zIG9uIGRhdGEgcGxvdAoKYGBge3J9Cm1vZDJfZW1tX3RpYiA8LSBtb2QyX2VtbSB8PgogIGFzX3RpYmJsZSgpIHw+CiAgcmVuYW1lKHNjb3JlID0gZW1tZWFuKQpgYGAKCmBgYHtyfQp0ZXN0X3N0dWR5MyB8PgogIGdncGxvdChhZXMoeCA9IG1ldGhvZCwgeSA9IHNjb3JlLCBmaWxsID0gbWV0aG9kLCBjb2xvdXIgPSBtZXRob2QpKSArCiAgZ2VvbV92aW9saW4oYWxwaGEgPSAwLjUpICsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuNSwgd2lkdGggPSAwLjIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpICsKICBnZW9tX2Vycm9yYmFyKGRhdGEgPSBtb2QyX2VtbV90aWIsIGFlcyh5bWluID0gbG93ZXIuQ0wsIHltYXggPSB1cHBlci5DTCksIGNvbG91ciA9ICdibGFjaycsIHdpZHRoID0gMC4yKSArCiAgZ2VvbV9wb2ludChkYXRhID0gbW9kMl9lbW1fdGliLCBjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMykgKwogIE5VTEwKYGBgCgoKIyMjIHBhaXJ3aXNlIGNvbXBhcmlzb25zCgpgYGB7cn0KbGV2ZWxzKHRlc3Rfc3R1ZHkzJG1ldGhvZCkKYGBgCgoKQXNzaWduIDEgdG8gdGhlIGxldmVsIHdlIGFyZSBpbnRlcmVzdGVkIGluLCBhbmQgLTEgdG8gdGhlIGJhc2VsaW5lIHdlIHdhbnQgdG8gY29tcGFyZSBpdCB0by4KCmBgYHtyfQptb2QyX2NvbXBhcmlzb25zIDwtIGxpc3QoCiAgJ1NlbGYtdGVzdCB2cy4gUmVhZCcgPSBjKC0xLCAxLCAwKSwgICMgcmVhZCBzZWxmLXRlc3Qgc3VtbWFyaXNlLCBpbiB0aGF0IG9yZGVyCiAgJ1N1bW1hcmlzZSB2cy4gUmVhZCcgPSBjKC0xLCAwLCAxKSwKICAnU2VsZi10ZXN0IHZzLiBTdW1tYXJpc2UnID0gYygwLCAxLCAtMSksCiAgJ21lYW4oU2VsZi10ZXN0LCBzdW1tYXJpc2UpIHZzLiBSZWFkJyA9IGMoLTEsIDAuNSwgMC41KSAgIyB3ZWlnaHRzIG11c3Qgc3VtIHRvIDAgLS0gcG9vbHMgc2VsZi10ZXN0IGFuZCBzdW1tYXJpc2UgdG9nIGFuZCBwaXRzIHRoYXQgbWVhbiBhZ2FpbnN0IG1lYW4gb2YgUmVhZAopCmBgYAoKCjEgLSAxIAoKYG1lYW4oU2VsZi10ZXN0KSAtIG1lYW4oUmVhZClgID0gcG9zaXRpdmUsIGJjIHNlbGYtdGVzdCBpcyBsYXJnZXIgdGhhbiByZWFkCgpgYGB7cn0KKG1vZDJfY29tcGFyaXNvbnNfdGVzdCA8LSBjb250cmFzdChtb2QyX2VtbSwgbW9kMl9jb21wYXJpc29ucykpCmBgYAoKYGNvbnRyYXN0KClgIGdpdmVzIHVzIHRoZSBwLXZhbHVlcyBmb3IgZWFjaCBkaWZmZXJlbmNlLgpXZSBjYW4gYWxzbyBnZXQgdGhlIDk1JSBDSXMgb2YgZWFjaCBkaWZmZXJlbmNlIGJ5IGdpdmluZyB0aGUgb3V0Y29tZSBvZiBgY29udHJhc3QoKWAgaW50byBgY29uZmludCgpYC4KCmBgYHtyfQpjb25maW50KG1vZDJfY29tcGFyaXNvbnNfdGVzdCkKYGBgCgoKCg==